package com.googlecode.cswish.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.WatchService;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.stereotype.Service;

import com.googlecode.cswish.struts.spring.SystemEnv;


@Service
public class FileSyncService {

	private static final Log logger = LogFactory.getLog(FileSyncService.class);
	
	@Resource
	@Qualifier(value="appScheduler")
	private ThreadPoolTaskScheduler taskScheduler;
	
	// TODO: use List<KeyValue> for the better read performance
	private Map<File, Collection<File>> syncFiles = new HashMap<>();
	
	private WatchService ws;
	
	public void addSyncFile(File fileFrom, File fileTo, boolean onlyForEclipseEnv) {
		if (onlyForEclipseEnv) {
			boolean isEclipse = "true".equals(System.getProperty(SystemEnv.RUNNING_IN_ECLIPSE));
			if (!isEclipse) {
				return ;
			}
		}
		
		Collection<File> syncList = syncFiles.get(fileFrom);
		if (syncList == null) {
			syncList = new HashSet<File>(2);			// most of the case, only one file
			syncFiles.put(fileFrom, syncList);
		}
		syncList.add(fileTo);
		
		/*
		try {
			Paths.get(fileFrom.getPath()).register(ws, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
		} catch (IOException ex) {
			logger.error("watch file system", ex);
		}*/
	}
	
	public void removeSyncFile(File fileFrom) {
		syncFiles.remove(fileFrom);
	}
	
	@PostConstruct
	private void init() {
		// TODO: use the operation system API?
		/*
		try {
			ws = FileSystems.getDefault().newWatchService();
		} catch (IOException ex) {
			logger.error("watch file system", ex);
		}*/

		Trigger trigger = new PeriodicTrigger(30_000); // each 30 second
		ScheduledFuture scheduler = taskScheduler.schedule(new Runnable() {
			@Override
			public void run() {
				try {
					long t1 = System.currentTimeMillis();
					boolean changed = false;
					for (Entry<File, Collection<File>> entry : syncFiles.entrySet()) {
						for (File to : entry.getValue()) {
							boolean ret = syncFile(entry.getKey(), to);
							if (ret) {
								changed = true;
							}
						}
					}
					long t2 = System.currentTimeMillis();
					long time = t2 - t1;
					if (time > 0 && logger.isDebugEnabled()) {
						if (changed) {
							logger.debug("File sychronization spent: " + time + "ms");
						}
					}
					
					/*
					WatchKey watchKey;
					do {
						watchKey = ws.take();
						for (WatchEvent<?> event : watchKey.pollEvents()) {
							Path eventPath = (Path)event.context();
							File file = eventPath.toFile();
						}
					} while (watchKey.reset());*/
				} catch (Exception ex) {
					logger.error("Run Scheduler", ex);
				}
			}
		}, trigger);
	}

	protected boolean syncFile(File from, File to) {
		boolean changed = false;
		if (from.isFile()) {
			long modifiedFrom = from.lastModified();
			long modifiedTo = to.lastModified();
			if (modifiedFrom == 0) {
				if (modifiedTo != 0) {
					to.delete();
					changed = true;
				}
			} else {
				if (modifiedFrom > modifiedTo) {
					try {
						FileUtils.copyFile(from, to, true);
						changed = true;
					} catch (IOException ex) {
						logger.error("Sync file", ex);
					}
				}
			}
		} else {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
			// scan the child folder
			for (File fromFile : from.listFiles()) {				// TODO: cache the list file?
				File toFile = new File(to, fromFile.getName());
				boolean ret = syncFile(fromFile, toFile);
				if (ret) {
					changed = true;
				}
			}
		}
		return changed;
	}
}